package io.wins.vertx.web;

import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jackson.JsonLoader;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.Message;
import io.vertx.core.eventbus.MessageConsumer;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JsonValidator extends AbstractVerticle {

    public static final String ADDRESS_VALIDATE_JSON = "io.wins.vertx.web#cmdValidateJson";


    private static final Logger logger = LoggerFactory.getLogger(JsonValidator.class);

    private final Map<String, String> validators = new HashMap<String, String>();
    private MessageConsumer<JsonObject> consumer;

    @Override
    public void start(final Future<Void> startFuture) throws Exception {
        final Future<List<String>> f = Future.future();

        vertx.fileSystem().readDir(config().getString("schemaDirectory"), f.completer());
        f.compose(filePaths -> {
            @SuppressWarnings("rawtypes") final List<Future> futures = new ArrayList<>();
            filePaths.forEach(filePath -> futures.add(createJsonSchemaValidator(filePath)));
            return CompositeFuture.all(futures);
        }).<Void>map(cf -> null).setHandler(startFuture.completer());

//        consumer = vertx.eventBus().consumer(JsonValidator.ADDRESS_VALIDATE_JSON, this::validateJson);
    }

    @Override
    public void stop(final Future<Void> stopFuture) throws Exception {
        consumer.unregister(stopFuture.completer());
    }

    private Future<String> createJsonSchemaValidator(final String filePath) {
        final Future<Buffer> f = Future.future();
        vertx.fileSystem().readFile(filePath, f.completer());

        return f.compose(fileContent -> {
            try {
                final Future<String> fc = Future.future();
                final String schemaId = resolveSchemaId(filePath);
                final JsonNode schema = JsonLoader.fromString(fileContent.toString());
//                validators.put(schemaId, String.create(schema)
//                        .enable(String.REMOVE_ADDITIONAL_PROPERTIES));
                JsonValidator.logger.info("Loaded json schema [{}]", schemaId);
                fc.complete(schemaId);
                return fc;
            } catch (final IOException e) {
                throw new UncheckedIOException(e);
            }
        });
    }

    private String resolveSchemaId(final String filePath) {
        final String fileName = filePath.substring(filePath.lastIndexOf(File.separator) + 1);
        return fileName.substring(0, fileName.lastIndexOf("."));
    }

    private void validateJson(final Message<JsonObject> message) {
        final String schemaId = message.headers().get("schemaId");
        if (!validators.containsKey(schemaId)) {
            message.fail(-1, "cannot find json schema");
            return;
        }

        try {
            final JsonNode json = JsonLoader.fromString(message.body().encode());
//            final Set<String> violations = validators.get(schemaId).validate(json);

//            if (violations.size() == 0) {
//                message.reply(new JsonObject(json.toString()));
//            } else {
//                message.fail(1, new JsonObject().put("errors", new JsonArray(violations.toString())).encode());
//            }
        } catch (Exception | Error e) {
            JsonValidator.logger.error("error", e);
            message.fail(-1, e.getMessage());
        }
    }

}
